'''
Created on Sep 29, 2011

@author: Andrzej
'''

import time
import re
import serial
import os
from PyDaq.Utils.Debug import dbg
from PyDaq.Hardware.InsteonCommands import CMDS, STANDARD_CMDS

PROMPT_FOUND = 1
PROMPT_NOT_FOUND = 0
PORT_OPEN = 1
PORT_CLOSED = 0
PORT_ERROR = -1
TIMEOUT = 1.0


def GetDecAddress(address):
    '''
    Convert hex address to decimal vector.  If address is in decimal format the command has no effect
    '''
    return [int(h) for h in address] 

class InsteonPLM(object):
    '''
    Class used to connect to Insteon serial power line modem (PLM) 
    '''
    
    debug = 4; # Controls verbosity output
    Interface = None  

    def __init__(self, port='/dev/ttyUSB0'):
        '''
        Constructor
        '''
        if os.name == 'nt':
            port = port -1
        try:        
            self.Ser = serial.Serial(port=port, baudrate=19200, timeout=3)
            self.Ser.timeout = 1
        except:
            self.Ser = None
            print "Error occured while oppening Serial Interface"
    
    def __del__(self):        
        if self.Ser.isOpen():
            self.Ser.close()
    
    def __unicode__(self):
        if self.Ser is not None:
            name = "PLM(%s)" %  self.Ser.port
        else:
            name = "PLM(None)"
            
        return name
            
    def isOpen(self):
        return self.Ser.isOpen()
    
    def __open__(self):
        if not(self.Ser.isOpen()):            
            try:
                self.Ser.open()
                dbg(self, 2, "Port %s is now open" % str(self.Ser.port))
                return PORT_OPEN
            except serial.SerialException:
                dbg(self, 0, "SerialException Cannot open port: %s " % str(self.Ser.port))
                return PORT_ERROR
            except:
                dbg(self, 0, "OtherException Cannot open port: %s" % str(self.Ser.port))
                return PORT_ERROR
        else:
            dbg(self, 2, "Port %s is already open" % str(self.Ser.port))
            return PORT_OPEN  
    
    ############################################################################################
    #
    #
    def send(self, cmd, get_confirmation=True):
        dbg(self, 2, "def send(self, cmd=%s):" % str(cmd))
        
        if not CMDS.has_key(cmd[0]):
            dbg_msg =  "Command %d does not exist in the dictionary" % cmd[0]
            dbg(self, 3, dbg_msg)
            return False
        else:
            cmd_details = CMDS.get(cmd[0])
            dbg_msg = "Sending: " + cmd_details[0] + " : " + str(cmd)
            dbg(self, 3, dbg_msg)
        
        if not self.isOpen():
            self.__open__()
        
        cmd.insert(0,0x2)
        hex_cmd = ''.join(chr(x) for x in cmd)        
        dbg_msg = "Full command: " + str(cmd)
        dbg(self, 3, dbg_msg)
        
        self.Ser.write(hex_cmd)
        
        if get_confirmation:
            return_data = self.read_response([cmd[1]])
#            response = ''
#            
#            n = 0
#            to = time.time()
#            tn = time.time() 
#            while tn - to < TIMEOUT and n < cmd_details[2]:                
#                n = self.Ser.inWaiting()
#                tn = time.time()
#                time.sleep(0.05)
#            
#            if tn - to > TIMEOUT:
#                dbg_msg = "Command timed out while waiting for response "
#                dbg(self, 3, dbg_msg)
#            
#            
#            if n >= cmd_details[2]:
#                n = self.Ser.inWaiting()
#                dbg_msg = "%d bytes inWaiting() reading out %d" % (n, cmd_details[2])
#                dbg(self, 4, dbg_msg)
#                response += self.Ser.read(cmd_details[2])        
#                #response += self.Ser.read(n)
#                data = [ord(x) for x in response]
#                dbg_msg = "cmd succesful: = %s" % str(data) #+ str(cmd[0:2] == data[0:2] and data[-1] == 6)
#                return_data = data[2:-1]            
#            else:
#                dbg_msg = "did not recive expected number of bytes within the time out period"
#                return_data = -1            
#            dbg(self, 3, dbg_msg)
        else:
            return_data = cmd
            
        return return_data
        
    def send_standard_cmd(self, hex_address, cmd1, cmd2):
        dbg(self, 1, "send_standard_cmd(self, hex_address, cmd1, cmd2):")
        cmd      = [98, 0, 0, 0, 15, 17, 255]
        cmd[1:4] = GetDecAddress(hex_address)
        cmd[5]   = cmd1
        cmd[6]   = cmd2
        sent_cmd = self.send(cmd)
        read_res = self.read_response([0x50])                
        return [sent_cmd, read_res]
    
    def read_response(self, cmd):
        dbg(self, 2, "def read_response(self, cmd%s):" % str(cmd))
        if not CMDS.has_key(cmd[0]):
            dbg_msg =  "Command %d does not exist in the dictionary" % cmd[0]
            dbg(self, 3, dbg_msg)
            return []
        else:
            cmd_details = CMDS.get(cmd[0])
            dbg_msg = "Waiting for response to: " + cmd_details[0] + " : " + str(cmd)
            dbg(self, 3, dbg_msg)
        
        if not self.isOpen():
            self.__open__()
        
        cmd_details = CMDS.get(cmd[0])
        to = time.time()
        tn = time.time()
        response = ''
        n = 0
        while tn - to < TIMEOUT and n < cmd_details[2]:
            n = self.Ser.inWaiting()
            tn = time.time()        
        
        if tn - to > TIMEOUT:
            dbg_msg = "Timeout occured while waiting for response"
            dbg(self, 3, dbg_msg)
        
        if n >= cmd_details[2]:
            #response += self.Ser.read(cmd_details[2])        
            response += self.Ser.read(n)
            data = [ord(x) for x in response]
            dbg_msg = "cmd succesful: = %s" % str(data) #+ str(cmd[0:2] == data[0:2] and data[-1] == 6)
            return_data = data[2:]
        else:
            dbg_msg = "did not recive expected number of bytes within the time out period"
            return_data = -1            
        dbg(self, 3, dbg_msg)
        return return_data
        
        
    def read(self):
        dbg(self, 2, "def read(self):")
        if self.Ser is not None:
            out = self.Ser.read(self.Ser.inWaiting())
            data = [ord(x) for x in out]
            dbg_msg = "read: %s" % str(data)
            dbg(self, 3, dbg_msg)
        else:
            dbg(self, 3, "No Serial connection")
            data = ''
        return data
    
    # Methods for specific modules
    
    def SetSwitchON(self, hex_address):
        dbg(self,1, "def SetSwitchON(self, hex_address=%s):" % str(hex_address))
        cmd         = [98, 0, 0, 0, 15, 17, 255]
        dec_address = [int(h) for h in hex_address]
        cmd[1:4]    = dec_address
        self.send(cmd)        
        return self.read_response([80])
    
    def SetSwitchOFF(self, hex_address):
        dbg(self,1, "def SetSwitchOFF(self, hex_address=%s):" % str(hex_address))
        cmd         = [98, 0, 0, 0, 15, 19, 255]
        dec_address = [int(h) for h in hex_address]
        cmd[1:4]    = dec_address        
        sent_status = self.send(cmd)
        read_status = self.read_response([80])
        return 
    
    def GetSwitchStatus(self, hex_address):
        cmd         = [98, 0, 0, 0, 15, 25, 255]
        dec_address = [int(h) for h in hex_address]
        cmd[1:4]    = dec_address        
        self.send(cmd)
        
        time.sleep(0.5)
        res = self.read()
        if res[-1] == 255:
            print "Switch is ON"
        elif res[-1] == 0:
            print "Switch is OFF"
        else:
            print "Switch status value: ", res[-1]
        return res
    
    def GetIdn(self):
        dbg(self, 1, "def GetIdn():")
        data = self.send([96])
        return data
       
        
if __name__ == '__main__':
    print "================================"
    print "PLM module test function"
    if os.name == 'nt':
        I = InsteonPLM(5)
    else:    
        I = InsteonPLM('/dev/ttyUSB1')
    
    #outdoor_sw = []
    appl_sw1   = [22, 131, 135]
    appl_sw    = [0x1b, 0x7A, 0x50]
    lamp_sw    = [0x18, 0x98, 0xAA]
    imeter     = [0x19, 0x89, 0x4E]
    outdoor_sw = [0x14, 0xA1, 0x28]
    
    test1 = True
    test2 = False
            
    if test1:
        I.send([2, 96])
        time.sleep(0.5)
        I.read()
        I.send([2, 98, 22, 131, 135, 15, 17, 255])
        time.sleep(0.5)
        I.read()
        time.sleep(0.5)
        I.send([2, 98, 22, 131, 135, 15, 19, 255])
        time.sleep(0.5)
        #CrossTheLine
        I.send([2, 98, 22, 131, 135, 15, 25, 255])
        time.sleep(0.5)
        I.read()
        
        #print "Turning of lamp 1"
        I.SetSwitchON(appl_sw);
        time.sleep(1)
        I.SetSwitchOFF(appl_sw);
        
        print "Turning of lamp 2"
        I.SetSwitchON(outdoor_sw);
        time.sleep(1)
        I.SetSwitchOFF(outdoor_sw);
    if test2:
        print "Test 2"
        I.send([0x60])